import java.lang.*;
import java.io.*;
import java.util.*;

import org.apache.commons.collections.*;

public class DecoupleObject 
{
	private void decouple(String obj2ObjMapFile, String localObj2ProjMapFile,
			String remoteObj2ProjMapFile) throws IOException
	{
		//read in the obj-proj map
		HashMap<String, String> localObj2ProjMap = new HashMap<String, String>();
		Utilities.readMapFromFile(localObj2ProjMap, localObj2ProjMapFile);
		
		//read in the obj-obj map
		MultiHashMap obj2ObjMap = new MultiHashMap();
		Utilities.readMultiMapFromFile(obj2ObjMap, obj2ObjMapFile);
		
		//System.out.println(obj2ObjMap.totalSize());
		
		//System.out.println(obj2ObjMap.totalSize() + " maps");
		//System.out.println(obj2ObjMap.size() + " coupled objs");
		
		HashSet<String> projSet = new HashSet<String>();
		
		HashSet<String> toBeRemovedObjSet = new HashSet<String>();
		
		/*
		//clean out the known obj
		Set<String> localObjSet = localObj2ProjMap.keySet(); 
		for (String key: localObjSet)
		{
			obj2ObjMap.remove(key);
			
		}
		
		//System.out.println(obj2ObjMap.totalSize() + " coupled maps after proning");
		//System.out.println(obj2ObjMap.size() + " coupled objs after proning");

		
		int kk = 0;
		
		//loop until all the single source objs are identitied the source
		int unResolvedObjSize = -1;
		while ((obj2ObjMap.size() != 0) && (unResolvedObjSize != obj2ObjMap.size()))
		{
			kk++;
			
			//assing the previous size to it
			unResolvedObjSize = obj2ObjMap.size();
			
			Set<String> objSet = obj2ObjMap.keySet();
			for (String objName: objSet)
			{
				Collection<String> basedObjCollection = 
					(Collection<String>)obj2ObjMap.get(objName);
				
				boolean continueFlag = false;
				
				Iterator it = basedObjCollection.iterator();
				while(it.hasNext())
				{
					String basedObj = (String) it.next();
					
					//it cannot be resolved in this loop
					if (localObj2ProjMap.containsKey(basedObj) == false)
					{
						//go to next loop
						continueFlag = true;
						
						break;
					}
					
					projSet.add(localObj2ProjMap.get(basedObj));
				}
				
				//since this one cannot be resolved in this loop
				//go to the next loop directly
				if (continueFlag == true)
				{
					projSet.clear();
					
					continue;
				}
				
				//it is a single-source obj, add it to the obj-proj map
				if (projSet.size() == 1 && Utilities.isOutObj(objName) == false)
				{
					Iterator projIt = projSet.iterator();
					String projName = (String)projIt.next();
					
					if (localObj2ProjMap.containsKey(objName))
					{
						if (localObj2ProjMap.get(objName).contains(projName))
						{
							continue;
						}
					}
			
					localObj2ProjMap.put(objName, projName);
					
					//remove it from the un-resolved obj-obj map
					toBeRemovedObjSet.add(objName);
				}
				
				projSet.clear();
			}
			
			for (String toBeRemovedObj: toBeRemovedObjSet)
			{
				obj2ObjMap.remove(toBeRemovedObj);
			}
			
			toBeRemovedObjSet.clear();
		}
		
		//System.out.println("still " + obj2ObjMap.size() + " objs left");
		//System.out.println("still " + obj2ObjMap.totalSize() + " maps left");
		//System.out.println(kk + " loops");
		
		toBeRemovedObjSet.clear();
		*/
		
		MultiHashMap remoteObj2ProjMap = new MultiHashMap();
		int unResolvedSize = -1;
		while(unResolvedSize != remoteObj2ProjMap.totalSize())
		{
			unResolvedSize = remoteObj2ProjMap.totalSize();
		
			Set<String> objSet = obj2ObjMap.keySet();
			for (String obj: objSet)
			{
				if (localObj2ProjMap.containsKey(obj) == true)
				{	
					continue;
				}
				
				if (Utilities.isOutObj(obj) == true)
				{
					//continue;
				}
				
				Collection<String> basedObjCollection = (Collection<String>)obj2ObjMap.get(obj);
		
				
				for (String basedObj: basedObjCollection)
				{
					if (localObj2ProjMap.containsKey(basedObj) == false)
					{
						continue;
					}
					
					if (localObj2ProjMap.containsKey(basedObj) == true)
					{
						String basedProj = localObj2ProjMap.get(basedObj);
						
						if (remoteObj2ProjMap.containsValue(obj, basedProj) == false)
						{
							remoteObj2ProjMap.put(obj, basedProj);
						}
					}
					
					if (remoteObj2ProjMap.containsKey(basedObj) == true)
					{
						Collection<String> basedProjCollection = (Collection<String>)remoteObj2ProjMap.get(basedObj);
						
						for (String basedProj: basedProjCollection)
						{
							if (remoteObj2ProjMap.containsValue(obj, basedProj) == false)
							{

								remoteObj2ProjMap.put(obj, basedProj);
							}
						}
					}
					
					/*
					if (localObj2ProjMap.containsKey(basedObj) == false)
					{
						toBeRemovedObjSet.add(obj);
						//System.out.println("----------");
						//System.out.println(obj);
						//System.out.println(basedObj);
						
						continue;
					}
					
					
					Collection<String> coll = (Collection<String>)remoteObj2ProjMap.get(obj);
					if (coll == null || coll.contains(localObj2ProjMap.get(basedObj)) == false)
					{
						if (Utilities.isDependency(obj) == true)
						{
							remoteObj2ProjMap.put(obj, localObj2ProjMap.get(basedObj));
						}
						
						//System.out.println("----------");
						//System.out.println(obj);
						//System.out.println(basedObj);
						//System.out.println(localObj2ProjMap.get(basedObj));
					}
					*/
				}
			}
		
		}
		
		System.out.println(remoteObj2ProjMap.totalSize());
		
		
		
		unResolvedSize = -1;
		while(unResolvedSize != remoteObj2ProjMap.totalSize())
		{
			unResolvedSize = remoteObj2ProjMap.totalSize();
		
			Set<String> objSet = obj2ObjMap.keySet();
			for (String obj: objSet)
			{
				if (localObj2ProjMap.containsKey(obj) == true)
				{	
					continue;
				}
				
				if (Utilities.isOutObj(obj) == false)
				{
					continue;
				}
				
				
				
				Collection<String> basedObjCollection = (Collection<String>)obj2ObjMap.get(obj);
				
				boolean nextLoop = false;
				for (String basedObj: basedObjCollection)
				{
					if (Utilities.isOutObj(basedObj) == true)
					{
						nextLoop = true;
					}
				}
		
				if (nextLoop == true)
				{
					continue;
				}
				
				for (String basedObj: basedObjCollection)
				{
							
					if (localObj2ProjMap.containsKey(basedObj) == false && remoteObj2ProjMap.containsKey(basedObj) == false)
					{
						continue;
					}
					
					if (localObj2ProjMap.containsKey(basedObj) == true)
					{
						String basedProj = localObj2ProjMap.get(basedObj);
						
						if (remoteObj2ProjMap.containsValue(obj, basedProj) == false)
						{
							remoteObj2ProjMap.put(obj, basedProj);
						}
					}
					
					if (remoteObj2ProjMap.containsKey(basedObj) == true)
					{
						Collection<String> basedProjCollection = (Collection<String>)remoteObj2ProjMap.get(basedObj);
						
						for (String basedProj: basedProjCollection)
						{
							if (remoteObj2ProjMap.containsValue(obj, basedProj) == false)
							{

								remoteObj2ProjMap.put(obj, basedProj);
							}
						}
					}
					
					/*
					if (localObj2ProjMap.containsKey(basedObj) == false)
					{
						toBeRemovedObjSet.add(obj);
						//System.out.println("----------");
						//System.out.println(obj);
						//System.out.println(basedObj);
						
						continue;
					}
					
					
					Collection<String> coll = (Collection<String>)remoteObj2ProjMap.get(obj);
					if (coll == null || coll.contains(localObj2ProjMap.get(basedObj)) == false)
					{
						if (Utilities.isDependency(obj) == true)
						{
							remoteObj2ProjMap.put(obj, localObj2ProjMap.get(basedObj));
						}
						
						//System.out.println("----------");
						//System.out.println(obj);
						//System.out.println(basedObj);
						//System.out.println(localObj2ProjMap.get(basedObj));
					}
					*/
				}
			}
		
		}
		
		System.out.println(remoteObj2ProjMap.totalSize());
		

		

		/*
		HashSet<String> toBeRemovedRemoteObjSet = new HashSet<String>();
		
		unResolvedSize = -1;
		while(unResolvedSize != remoteObj2ProjMap.totalSize())
		{
			unResolvedSize = remoteObj2ProjMap.totalSize();
			toBeRemovedRemoteObjSet.clear();
			
			for (String obj: (Set<String>)obj2ObjMap.keySet())
			{
				//if (Utilities.isOutObj(obj) == false)
				if (remoteObj2ProjMap.containsKey(obj) == false)
				{
					toBeRemovedObjSet.add(obj);
					
					continue;
				}
				
				boolean nextLoopFlag = false;
				for (String basedObj: (Collection<String>)obj2ObjMap.getCollection(obj))
				{
					//if (Utilities.isOutObj(basedObj) == false)
					if (remoteObj2ProjMap.containsKey(basedObj) == false)
					{
						nextLoopFlag = true;
						
						break;
					}
				}
				
				if (nextLoopFlag == true)
				{
					continue;
				}
				
				Collection<String> projCollection = 
					(Collection<String>)remoteObj2ProjMap.getCollection(obj);
				if (projCollection == null)
				{
					continue;
				}
				
				for (String basedObj: projCollection)
				{
					if (remoteObj2ProjMap.containsKey(basedObj) == false)
					{
						//toBeRemovedRemoteObjSet.add(obj);
						
						break;
					}
					
					Collection<String> basedObjCollection = 
						(Collection<String>)remoteObj2ProjMap.getCollection(basedObj);
					for (String proj: basedObjCollection)
					{
						Collection<String> coll = (Collection<String>)remoteObj2ProjMap.get(obj);
						if (coll.contains(proj) == false)
						{
							remoteObj2ProjMap.put(obj, proj);
						}
					}
				}
			}
			
			for (String toBeRemovedRemoteObj: toBeRemovedRemoteObjSet)
			{
				remoteObj2ProjMap.remove(toBeRemovedRemoteObj);
			}
			
		}
		
		System.out.println(remoteObj2ProjMap.size());
		*/
		
		
		
		/*
		Set<String> remoteObjSet = remoteObj2ProjMap.keySet();
		System.out.println(remoteObjSet.size());
		
		
		MultiHashMap suffixSet = new MultiHashMap();
		
		int iiii = 0;
		for (String remoteObj: remoteObjSet)
		{
			String words[] = remoteObj.split("_");
			
			boolean flag = false;
			
			for (String word: words)
			{
				if(word.toUpperCase().equals("LIBRARIES") == true)
				{
					flag = true;
				}
			}
			
			if (flag == false)
			{
				if (Utilities.isDependency(remoteObj) == false)
				{
					System.out.println(remoteObj);
				}
			}
			
			//Collection<String> coll = (Collection<String>) remoteObj2ProjMap.get(key);
			//System.out.println(key);
			
			if (Utilities.isOutObj(remoteObj) == false)
			{
				continue;
			}
			
			String suffix = remoteObj.substring(remoteObj.lastIndexOf('_') + 1, remoteObj.length());
			suffixSet.put(suffix, "1");
			
			if (suffix.equals("gen"))
			{
				//System.out.println(remoteObj);
			}
			//iiii++;
		}
		System.out.println(remoteObjSet.size() - iiii);
		
		Set<String> set = suffixSet.keySet();
		for (String suffix: set)
		{
			//System.out.println(suffix + " " + suffixSet.getCollection(suffix).size());
		}
		*/
		
		//Utilities.writeMapToFile(localObj2ProjMap, localObj2ProjMapFile);
		Utilities.writeMultiMapToFile(remoteObj2ProjMap, remoteObj2ProjMapFile);
	}
	
	public static void main(String args[]) throws IOException
	{
		DecoupleObject fuyun = new DecoupleObject();
		fuyun.decouple("data/android-4.0.1/obj2ObjMap.csv", 
				"data/android-4.0.1/localObj2ProjMap.csv", "data/android-4.0.1/remoteObj2ProjMap.csv");
	}
}
